package com.aceitunaproject.server.map;

import java.awt.Point;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.aceitunaproject.server.ServerConfiguration;
import com.aceitunaproject.server.entity.settlement.Settlement;
import com.aceitunaproject.server.map.MapTile;
import com.sun.sgs.app.AppContext;
import com.sun.sgs.app.DataManager;
import com.sun.sgs.app.ManagedReference;

/**
 * World map manager interface implementation
 * 
 * @author Maciej Miechowicz
 */
public class MapManagerImpl implements MapManager {

	private static final long serialVersionUID = 1L;

	/**
	 * The map type.
	 */
	private MapType mapType = MapType.IsometricTiles;

	/**
	 * @return the mapType
	 */
	public MapType getMapType() {
		return mapType;
	}

	/**
	 * @param mapType the mapType to set
	 */
	public void setMapType(MapType mapType) {
		this.mapType = mapType;
	}

	private Map<Point, ManagedReference<MapTile>> worldMap = new HashMap<Point, ManagedReference<MapTile>>();

	/**
	 * Creates a new instance of the manager
	 * 
	 */
	public MapManagerImpl() {

	}

	// Returns the current WorldMap
	private Map<Point, ManagedReference<MapTile>> getWorldMap() {

		return worldMap;
	}

	@Override
	public MapTile getTile(Point position) {
		MapTile retMapTile = null;
		ManagedReference<MapTile> tileRef = getWorldMap().get(position);
		if (tileRef != null) {
			retMapTile = tileRef.get();
		}
		return retMapTile;

	}

	@Override
	public MapTile getTile(int x, int y) {

		return getTile(new Point(x, y));
	}

	@Override
	public LinkedList<MapTile> getAllTiles() {
		LinkedList<MapTile> retList = new LinkedList<MapTile>();

		for (ManagedReference<MapTile> mapTileRef : getWorldMap().values()) {

			retList.add(mapTileRef.get());
		}
		return retList;
	}

	@Override
	public void createWorldMap(int xDim, int yDim) {
		DataManager dataManager = AppContext.getDataManager();
		for (int i = 0; i < xDim; i++) {
			for (int n = 0; n < yDim; n++) {
				MapTile mt = new MapTile(i, n);
				worldMap.put(new Point(i, n), dataManager.createReference(mt));

			}

		}
	}

	@Override
	public List<MapTile> getAdjacentTiles(MapTile tile) {

		List<MapTile> result = null;
		switch (mapType) {
		case HexagonalTiles:
			result = getHexagonalAdjacentTiles(tile);
			break;

		case RectangularTiles:
			result = getRectangularAdjacentTiles(tile);
			break;
			
		case IsometricTiles:
			result = getIsometricAdjacentTiles(tile);
			break;

		}
		return result;
	}

	private List<MapTile> getIsometricAdjacentTiles(MapTile tile) {

		List<MapTile> result = new LinkedList<MapTile>();

		if((tile.getYPos() & 1) !=0)//odd
		{
			addIfNotNull(result,getTile(tile.getXPos(), tile.getYPos()-2));// N ( 0, -2, 0, -2),
			addIfNotNull(result,getTile(tile.getXPos()+1, tile.getYPos()-1));// NE ( 1, -1, 0, -1),
	    	addIfNotNull(result,getTile(tile.getXPos()+1, tile.getYPos()));// E ( 1, 0, 1, 0),
			addIfNotNull(result,getTile(tile.getXPos()+1, tile.getYPos()+1));// SE ( 1, 1, 0, 1),
			addIfNotNull(result,getTile(tile.getXPos(), tile.getYPos()+2));// S ( 0, 2, 0, 2),
			addIfNotNull(result,getTile(tile.getXPos(), tile.getYPos()+1));// SW ( 0, 1
			addIfNotNull(result,getTile(tile.getXPos()-1, tile.getYPos()));// W (-1, 0
			addIfNotNull(result,getTile(tile.getXPos(), tile.getYPos()-1));// NW ( 0, -1
		}
		else//even
		{
			addIfNotNull(result,getTile(tile.getXPos(), tile.getYPos()-2));// N (0, -2),
			addIfNotNull(result,getTile(tile.getXPos(), tile.getYPos()-1));// NE ( 0, -1),
	    	addIfNotNull(result,getTile(tile.getXPos()+1, tile.getYPos()));// E ( 1, 0),
			addIfNotNull(result,getTile(tile.getXPos(), tile.getYPos()+1));// SE (  0, 1),
			addIfNotNull(result,getTile(tile.getXPos(), tile.getYPos()+2));// S (  0, 2),
			addIfNotNull(result,getTile(tile.getXPos()-1, tile.getYPos()+1));// SW (  -1, 1),
			addIfNotNull(result,getTile(tile.getXPos()-1, tile.getYPos()));// W ( -1, 0),
			addIfNotNull(result,getTile(tile.getXPos()-1, tile.getYPos()-1));// NW (  -1, -1);
		}
		
		return result;
		// N ( 0, -2, 0, -2),
		// NE ( 1, -1, 0, -1),
		// E ( 1, 0, 1, 0),
		// SE ( 1, 1, 0, 1),
		// S ( 0, 2, 0, 2),
		// SW ( 0, 1, -1, 1),
		// W (-1, 0, -1, 0),
		// NW ( 0, -1, -1, -1);
	}

	private List<MapTile> getRectangularAdjacentTiles(MapTile tile) {
		List<MapTile> result = new LinkedList<MapTile>();

		// TopLeft
		addIfNotNull(result, getTile(tile.getXPos() - 1, tile.getYPos() - 1));

		// Top
		addIfNotNull(result, getTile(tile.getXPos(), tile.getYPos() - 1));

		// TopRight
		addIfNotNull(result, getTile(tile.getXPos() + 1, tile.getYPos() - 1));

		// Left
		addIfNotNull(result, getTile(tile.getXPos() - 1, tile.getYPos()));

		// Right
		addIfNotNull(result, getTile(tile.getXPos() + 1, tile.getYPos()));

		// BottomLeft
		addIfNotNull(result, getTile(tile.getXPos() - 1, tile.getYPos() + 1));

		// Bottom
		addIfNotNull(result, getTile(tile.getXPos(), tile.getYPos() + 1));

		// BottomRight
		addIfNotNull(result, getTile(tile.getXPos() + 1, tile.getYPos() + 1));

		return result;
	}

	private void addIfNotNull(List<MapTile> result, MapTile tile) {
		if (tile != null)
			result.add(tile);

	}

	private List<MapTile> getHexagonalAdjacentTiles(MapTile tile) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public long getInstallationLimit(MapTile tile) {
		long result = 1;
		Settlement settlement = tile.getSettlement();
		if(settlement!=null)
		{
			result = settlement.getPersons()/ServerConfiguration.getInstallationPersonRate();
		}
			
		return result;
	}
}
